/*
 * DecimalTests.java
 *
 *  
 */
package org.msb.finance.util;

import static org.junit.Assert.*;

import java.text.NumberFormat;
import java.util.Locale;

import org.junit.Before;
import org.junit.Test;

/**
 * 
 * 
 * @author Marc Boudreau
 */
@SuppressWarnings("nls")
public class DecimalTests {

	private Decimal d;

	/**
	 * @throws Exception
	 */
	@Before
	public void setUp() throws Exception {
		this.d = new Decimal(12345678, createNumberFormat(4));
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalDefaultConstructor() throws Exception {
		assertNotNull(new Decimal());
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalConstructorWithInt() throws Exception {
		assertNotNull(this.d);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalConstructorWithNegativeInt() throws Exception {
		assertNotNull(new Decimal(-8459374));
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalCopyConstructor() throws Exception {
		Decimal copy = new Decimal(this.d);

		assertNotNull(copy);
		assertNotSame(this.d, copy);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalToString() throws Exception {
		assertEquals("1234.5678", this.d.toString());
		assertSame(this.d.toString(), this.d.toString());
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalToStringCaching() throws Exception {
		Decimal d2 = new Decimal(12345678);

		String s1 = d2.toString();
		d2.setNumberFormat(createNumberFormat(2));
		String s2 = d2.toString();

		assertNotSame(s1, s2);
		assertTrue(s1.equals(s2));
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalToStringForZero() throws Exception {
		Decimal d2 = new Decimal(0);

		assertEquals("0.00", d2.toString());
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalToStringForNegative() throws Exception {
		Decimal d2 = new Decimal(-304562);

		assertEquals("-30.46", d2.toString());
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalToStringForLessThanOne() throws Exception {
		Decimal d2 = new Decimal(3489);

		assertEquals("0.35", d2.toString());
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalToFloat() throws Exception {
		assertEquals(1234.5678f, this.d.toFloat(), 0.00001);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalToFloatForZero() throws Exception {
		Decimal d2 = new Decimal(0);

		assertEquals(0f, d2.toFloat(), 0.00001);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalToFloatForNegative() throws Exception {
		Decimal d2 = new Decimal(-304562);

		assertEquals(-30.4562f, d2.toFloat(), 0.00001);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalToFloatForLessThanOne() throws Exception {
		Decimal d2 = new Decimal(3489);

		assertEquals(0.3489f, d2.toFloat(), 0.00001);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalSetFractionDigits() throws Exception {
		Decimal d0 = new Decimal(10005555, createNumberFormat(0));
		Decimal d1 = new Decimal(d0, createNumberFormat(1));
		Decimal d2 = new Decimal(d0, createNumberFormat(2));
		Decimal d3 = new Decimal(d0, createNumberFormat(3));
		Decimal d4 = new Decimal(d0, createNumberFormat(4));

		assertEquals("1001", d0.toString());
		assertEquals("1000.6", d1.toString());
		assertEquals("1000.56", d2.toString());
		assertEquals("1000.556", d3.toString());
		assertEquals("1000.5555", d4.toString());
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalGetNumberFormat() throws Exception {
		assertNotNull(this.d.getNumberFormat());

		Decimal d2 = new Decimal(0, createNumberFormat(8));
		assertEquals(8, d2.getNumberFormat().getMaximumFractionDigits());
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalAddDecimal() throws Exception {
		Decimal d2 = new Decimal(10000021);

		String s1 = this.d.toString();
		String s2 = this.d.add(d2).toString();
		assertEquals("2234.5699", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalAddZeroDecimal() throws Exception {
		Decimal d2 = new Decimal(0);

		String s1 = this.d.toString();
		String s2 = this.d.add(d2).toString();
		assertEquals("1234.5678", s2);
		assertSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalAddNegativeDecimal() throws Exception {
		Decimal d2 = new Decimal(-10000000);

		String s1 = this.d.toString();
		String s2 = this.d.add(d2).toString();
		assertEquals("234.5678", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalAddLargerNegativeDecimal() throws Exception {
		Decimal d2 = new Decimal(-56781232);

		String s1 = this.d.toString();
		String s2 = this.d.add(d2).toString();
		assertEquals("-4443.5554", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalAddInt() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.add(1000).toString();
		assertEquals("2234.5678", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalAddZeroInt() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.add(0).toString();
		assertEquals("1234.5678", this.d.add(0).toString());
		assertSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalAddNegativeInt() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.add(-1000).toString();
		assertEquals("234.5678", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalAddLargerNegativeInt() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.add(-5678).toString();
		assertEquals("-4443.4322", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalAddFloat() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.add(1000.0210f).toString();
		assertEquals("2234.5888", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalAddZeroFloat() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.add(0f).toString();
		assertEquals("1234.5678", s2);
		assertSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalAddNegativeFloat() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.add(-1000f).toString();
		assertEquals("234.5678", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalAddLargerNegativeFloat() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.add(-5678.1232f).toString();
		assertEquals("-4443.5554", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalAddNumber() throws Exception {
		Number n = new Float(2590.9178f);

		String s1 = this.d.toString();
		String s2 = this.d.add(n).toString();
		assertEquals("3825.4856", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalSubtractDecimal() throws Exception {
		Decimal d2 = new Decimal(583925);

		String s1 = this.d.toString();
		String s2 = this.d.subtract(d2).toString();
		assertEquals("1176.1753", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalSubtractZeroDecimal() throws Exception {
		Decimal d2 = new Decimal(0);

		String s1 = this.d.toString();
		String s2 = this.d.subtract(d2).toString();
		assertEquals("1234.5678", s2);
		assertSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalSubtractEqualDecimal() throws Exception {
		Decimal d2 = new Decimal(12345678);

		String s1 = this.d.toString();
		String s2 = this.d.subtract(d2).toString();
		assertEquals("0.0000", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalSubtractInt() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.subtract(204).toString();
		assertEquals("1030.5678", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalSubtractZeroInt() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.subtract(0).toString();
		assertEquals("1234.5678", s2);
		assertSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalSubtractEqualInt() throws Exception {
		Decimal d2 = new Decimal(12340000);

		String s1 = d2.toString();
		String s2 = d2.subtract(1234).toString();
		assertEquals("0.00", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalSubtractFloat() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.subtract(58.3925f).toString();
		assertEquals("1176.1753", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalSubtractZeroFloat() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.subtract(0f).toString();
		assertEquals("1234.5678", s2);
		assertSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalSubtractEqualFloat() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.subtract(1234.5678f).toString();
		assertEquals("0.0000", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalSubtractNumber() throws Exception {
		Number n = new Float(2590.9178f);

		String s1 = this.d.toString();
		String s2 = this.d.subtract(n).toString();
		assertEquals("-1356.3500", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalMultiplyDecimal() throws Exception {
		Decimal d2 = new Decimal(30001);

		String s1 = this.d.toString();
		String s2 = this.d.multiply(d2).toString();
		assertEquals("3703.8268", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalMultiplyByOneDecimal() throws Exception {
		Decimal d2 = new Decimal(10000);

		String s1 = this.d.toString();
		String s2 = this.d.multiply(d2).toString();
		assertEquals("1234.5678", s2);
		assertSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalMultiplyByZeroDecimal() throws Exception {
		Decimal d2 = new Decimal(0);

		String s1 = this.d.toString();
		String s2 = this.d.multiply(d2).toString();
		assertEquals("0.0000", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalMultiplyByNegativeOneDecimal() throws Exception {
		Decimal d2 = new Decimal(-10000);

		String s1 = this.d.toString();
		String s2 = this.d.multiply(d2).toString();
		assertEquals("-1234.5678", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalMultiplyByLessThanOneDecimal() throws Exception {
		Decimal d2 = new Decimal(3333);

		String s1 = this.d.toString();
		String s2 = this.d.multiply(d2).toString();
		assertEquals("411.4814", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalMultiplyInt() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.multiply(3).toString();
		assertEquals("3703.7034", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalMultiplyByOneInt() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.multiply(1).toString();
		assertEquals("1234.5678", s2);
		assertSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalMultiplyByZeroInt() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.multiply(0).toString();
		assertEquals("0.0000", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalMultiplyByNegativeOneInt() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.multiply(-1).toString();
		assertEquals("-1234.5678", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalMultiplyFloat() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.multiply(3.0001f).toString();
		assertEquals("3703.8268", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalMultiplyByOneFloat() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.multiply(1f).toString();
		assertEquals("1234.5678", s2);
		assertSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalMultiplyByZeroFloat() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.multiply(0f).toString();
		assertEquals("0.0000", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalMultiplyByNegativeOneFloat() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.multiply(-1f).toString();
		assertEquals("-1234.5678", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalMultiplyByLessThanOneFloat() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.multiply(0.5f).toString();
		assertEquals("617.2839", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalMultiplyNumber() throws Exception {
		Number n = new Float(2f);

		String s1 = this.d.toString();
		String s2 = this.d.multiply(n).toString();
		assertEquals("2469.1356", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalDivideDecimal() throws Exception {
		Decimal d2 = new Decimal(20453);

		String s1 = this.d.toString();
		String s2 = this.d.divide(d2).toString();
		assertEquals("603.6121", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalDivideByZeroDecimal() throws Exception {
		Decimal d2 = new Decimal(0);

		try {
			this.d.divide(d2);
			fail("Expected an ArithmeticException to be thrown for dividing Decimal by zero.");
		} catch (ArithmeticException ex) {
			// Pass.
		}
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalDivideByOneDecimal() throws Exception {
		Decimal d2 = new Decimal(10000);

		String s1 = this.d.toString();
		String s2 = this.d.divide(d2).toString();
		assertEquals("1234.5678", s2);
		assertSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalDivideByNegativeOneDecimal() throws Exception {
		Decimal d2 = new Decimal(-10000);

		String s1 = this.d.toString();
		String s2 = this.d.divide(d2).toString();
		assertEquals("-1234.5678", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalDivideInt() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.divide(2).toString();
		assertEquals("617.2839", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalDivideByZeroInt() throws Exception {
		try {
			this.d.divide(0);
			fail("Expected ArithmeticException to be thrown for dividing Decimal by zero.");
		} catch (ArithmeticException ex) {
			// Pass.
		}
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalDivideByOneInt() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.divide(1).toString();
		assertEquals("1234.5678", s2);
		assertSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalDivideByNegativeOneInt() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.divide(-1).toString();
		assertEquals("-1234.5678", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalDivideFloat() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.divide(2.0453f).toString();
		assertEquals("603.6121", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalDivideByOneFloat() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.divide(1f).toString();
		assertEquals("1234.5678", s2);
		assertSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalDivideByZeroFloat() throws Exception {
		try {
			this.d.divide(0f);
			fail("Expected an ArithmeticException to be thrown for dividing Decimal by zero.");
		} catch (ArithmeticException ex) {
			// Pass.
		}
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalDivideByNegativeOneFloat() throws Exception {
		String s1 = this.d.toString();
		String s2 = this.d.divide(-1f).toString();
		assertEquals("-1234.5678", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalDivideNumber() throws Exception {
		Number n = new Float(1.0389);

		String s1 = this.d.toString();
		String s2 = this.d.divide(n).toString();
		assertEquals("1188.3413", s2);
		assertNotSame(s1, s2);
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalFormat() throws Exception {
		NumberFormat formatter = NumberFormat.getNumberInstance(Locale.GERMAN);
		formatter.setMinimumFractionDigits(3);
		formatter.setMaximumFractionDigits(3);

		assertEquals("1.234,568", this.d.format(formatter));
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalEquals() throws Exception {
		Decimal d1 = this.d;
		Decimal d2 = new Decimal(this.d);
		Decimal d3 = new Decimal(d2);
		Decimal d4 = new Decimal(9832354);

		assertFalse(this.d.equals(null));
		assertTrue(this.d.equals(this.d));
		assertTrue(this.d.equals(d1));
		assertTrue(d1.equals(this.d));

		assertTrue(this.d.equals(d2));
		assertTrue(d2.equals(this.d));
		assertTrue(d2.equals(d3));
		assertTrue(d3.equals(d2));
		assertTrue(d3.equals(this.d));
		assertTrue(this.d.equals(d3));

		assertFalse(this.d.equals(d4));
		assertFalse(d4.equals(this.d));
	}

	/**
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDecimalHashCode() throws Exception {
		Decimal d1 = this.d;
		Decimal d2 = new Decimal(this.d);
		Decimal d3 = new Decimal(d2);
		Decimal d4 = new Decimal(9832354);

		assertTrue(this.d.hashCode() == d1.hashCode());
		assertTrue(this.d.hashCode() == d2.hashCode());
		assertTrue(this.d.hashCode() == d3.hashCode());
		assertFalse(this.d.hashCode() == d4.hashCode());

	}

	/*
	 * 
	 */
	private static NumberFormat createNumberFormat(int fractionDigits) {
		NumberFormat formatter = NumberFormat.getNumberInstance();
		formatter.setMaximumFractionDigits(fractionDigits);
		formatter.setMinimumFractionDigits(fractionDigits);
		formatter.setGroupingUsed(false);

		return formatter;
	}
}
